import fs from "../fs"
import stat from '../util/stat'
import remove from '../remove'
import mkdirs from '../mkdirs'
import copy from '../copy'
import path from '../../node/path'
import pathExists from '../path-exists'
import { checkError } from '../../universalify'
import errorCode from '../../node/error-code'

export default function move (src, dest, opts, cb?) {
    if (typeof opts === 'function') {
        cb = opts
        opts = {}
    }

    opts = opts || {}

    const overwrite = opts.overwrite || opts.clobber || false

    stat.checkPaths(src, dest, 'move', opts, (err, stats) => {
        if (checkError(err)) return cb(err)
        const { srcStat, isChangingCase = false } = stats
        let destParent = path.dirname(dest)
        stat.checkParentPaths(src, srcStat, dest, 'move', err => {
            if (checkError(err)) return cb(err)
            if (isParentRoot(dest) || pathExists.pathExistsSync(destParent)) {
                return doRename(src, dest, overwrite, isChangingCase, cb)
            }
            pathExists.pathExists(destParent, (err, dirExists) => {
                if (checkError(err)) {
                    mkdirs.mkdirp(path.dirname(dest), err => {
                        if (checkError(err)) return cb(err)
                        return doRename(src, dest, overwrite, isChangingCase, cb)
                    })
                }
                if (dirExists) {
                    return doRename(src, dest, overwrite, isChangingCase, cb)
                }
            })
        })
    })
}

function isParentRoot (dest) {
    const parent = path.dirname(dest)
    const parsedPath = path.parse(parent)
    return parsedPath.root === parent
}

function doRename (src, dest, overwrite, isChangingCase, cb) {
    if (isChangingCase) return rename(src, dest, overwrite, cb)
    if (overwrite) {
        return remove.remove(dest, err => {
            if (checkError(err)) return cb(err)
            return rename(src, dest, overwrite, cb)
        })
    }
    pathExists.pathExists(dest, (err, destExists) => {
        if (checkError(err)) return cb(err)
        if (destExists) return cb(new Error('dest already exists'))
        return rename(src, dest, overwrite, cb)
    })
}

function rename (src, dest, overwrite, cb) {
    fs.rename(src, dest, err => {
        if (!checkError(err)) return cb()
        if (err.message !== errorCode.errormessage.EXDEV) return cb(err)
        return moveAcrossDevice(src, dest, overwrite, cb)
    })
}

function moveAcrossDevice (src, dest, overwrite, cb) {
    const opts = {
        overwrite,
        errorOnExist: true
    }
    copy.copy(src, dest, opts, err => {
        if (checkError(err)) return cb(err)
        return remove.remove(src, cb)
    })
}

